﻿using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections;

namespace EastFantasy.EasyEngine.Public
{
    public delegate void HotkeyEventHandler(int hotKeyId);

    public class Hotkeys : IMessageFilter
    {
        public event HotkeyEventHandler OnHotkey;

        public enum KeyFlags
        {
            MOD_NONE = 0x0,
            MOD_ALT = 0x1,
            MOD_CONTROL = 0x2,
            MOD_SHIFT = 0x4,
            MOD_WIN = 0x8
        }

        private class NativeMethods
        {
            private NativeMethods()
            {
            }

            #region WIN32 API

            /// <summary>   
            /// 如果函数执行成功，返回值不为0。   
            /// 如果函数执行失败，返回值为0。要得到扩展错误信息，调用GetLastError。.NET方法:Marshal.GetLastWin32Error()   
            /// </summary>   
            /// <param name="hWnd">要定义热键的窗口的句柄</param>   
            /// <param name="id">定义热键ID（不能与其它ID重复） </param>   
            /// <param name="fsModifiers">标识热键是否在按Alt、Ctrl、Shift、Windows等键时才会生效</param>   
            /// <param name="vk">定义热键的内容,WinForm中可以使用Keys枚举转换，   
            /// WPF中Key枚举是不正确的,应该使用System.Windows.Forms.Keys枚举，或者自定义正确的枚举或int常量</param>   
            /// <returns></returns>   
            [DllImport("user32.dll", SetLastError = true)]
            public static extern bool RegisterHotKey(
                IntPtr hWnd,
                UInt32 id,
                UInt32 fsModifiers,
                UInt32 vk
                );

            /// <summary>   
            /// 取消注册热键   
            /// </summary>   
            /// <param name="hWnd">要取消热键的窗口的句柄</param>   
            /// <param name="id">要取消热键的ID</param>   
            /// <returns></returns>   
            [DllImport("user32.dll", SetLastError = true)]
            public static extern bool UnregisterHotKey(
                IntPtr hWnd,
                UInt32 id
                );

            /// <summary>   
            /// 向全局原子表添加一个字符串，并返回这个字符串的唯一标识符,成功则返回值为新创建的原子ID,失败返回0   
            /// </summary>   
            /// <param name="lpString"></param>   
            /// <returns></returns>   
            [DllImport("kernel32", SetLastError = true)]
            public static extern UInt32 GlobalAddAtom(string lpString);

            /// <summary>  
            /// 从全局原子表删除指定一个字符串，并返回这个字符串的唯一标识符,失败返回0  
            /// </summary>  
            /// <param name="nAtom"></param>  
            /// <returns></returns>  
            [DllImport("kernel32", SetLastError = true)]
            public static extern UInt32 GlobalDeleteAtom(UInt32 nAtom);

            /// <summary>   
            /// 定义了辅助键的名称（将数字转变为字符以便于记忆，也可去除此枚举而直接使用数值）   
            /// </summary>  
            public enum KeyFlags
            {
                MOD_NONE = 0x0,
                MOD_ALT = 0x1,
                MOD_CONTROL = 0x2,
                MOD_SHIFT = 0x4,
                MOD_WIN = 0x8
            }

            #endregion
        }

        private Hashtable keyIDs = new Hashtable();
        private IntPtr hWnd;

        public Hotkeys(IntPtr hWnd)
        {
            this.hWnd = hWnd;
            Application.AddMessageFilter(this);
        }

        public UInt32 RegisterHotkey(Keys Key, KeyFlags keyflags)
        {
            UInt32 hotkeyid = NativeMethods.GlobalAddAtom(System.Guid.NewGuid().ToString());
            NativeMethods.RegisterHotKey((IntPtr) hWnd, hotkeyid, (UInt32) keyflags, (UInt32) Key);
            keyIDs.Add(hotkeyid, hotkeyid);
            return (UInt32) hotkeyid;
        }

        public void UnregisterHotkeys()
        {
            Application.RemoveMessageFilter(this);
            foreach (UInt32 key in keyIDs.Values)
            {
                NativeMethods.UnregisterHotKey(hWnd, key);
                NativeMethods.GlobalDeleteAtom(key);
            }
        }

        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == 0x312)
            {
                if (OnHotkey != null)
                {
                    foreach (UInt32 key in keyIDs.Values)
                    {
                        if ((UInt32) m.WParam == key)
                        {
                            OnHotkey((int) m.WParam);
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
}